class AI{
	protected var _isInRange:boolean;
	
	protected var _playersInRange:Array = new Array();
	protected var _playerList:Array = new Array();
	
	protected var _flankablePlayers:Array = new Array();
	protected var _detectFlankRadius:int;	
	protected var _previousFlankable:Array;
	
	protected var _atkRange:int;
	protected var _srcCoord:Vector2;
	
	protected var _closestPlayer:GameCharacter;
	protected var _coord:Vector2;
	protected var _closestDistance;
	
	protected var _selectedEnemy:EnemyCharacter;
	
	protected var _closestCover:Tile;	
	
	public function AI(selectedEnemy:EnemyCharacter, playerList:Array){
		_selectedEnemy = selectedEnemy;
		
		_isInRange = false;
		_closestDistance = 999999;
		_atkRange = selectedEnemy.getAttackRadius();
	 	_srcCoord = selectedEnemy.getCoord();
	 	_closestCover = null;
	 	_closestPlayer = null;
	 	_playerList = playerList;
	 	
	 	//Initial flank radius is move radius+sight radius
	 	//New flank radius should minus previous distance moved by enemy
	 	_detectFlankRadius = selectedEnemy.getMoveRadius() + selectedEnemy.getSightRadius(); 
	 	_previousFlankable = _selectedEnemy.getPreviousFlankable();	
	}
	
	//Getters
	public function getIsInRange():boolean{ return _isInRange; }
	public function getPlayersInRange():Array{ return _playersInRange; }
	public function getClosestPlayer():GameCharacter{ return _closestPlayer; }
	public function getClosestDistance(){ return _closestDistance; }
	
	function getCoverTile(minDistance, mapReference):Tile{
		
		mapReference.checkRangeFrom(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var coverList:Array = mapReference.getCoverTiles(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var closestCovers:Array = new Array();
		
		var closestCoverDistance = 9999999;
		var currentDistance;
		
		// Set to run to closest tile by default
		var closestCover:Tile = null;	
		
		var furthestCoverDistance = 0;
		var currentFurthestDistance;
		
		// Find furthest cover
		var furthestCover:Tile = null;
		
		if(_selectedEnemy.getTurnsLeft() == 2){
			_selectedEnemy.setPreviousTile(_selectedEnemy.getTileReference());
		}
		
		for(var i:int=0; i<coverList.length; i++){
			var current_tile:Tile = coverList[i];
			//Debug.Log("Cover Tile: "+current_tile);
			if(!isTileFlankedByPlayer(current_tile)){				
				currentDistance = Vector2.Distance(current_tile.getCoord(), _coord);
				if(currentDistance > minDistance && currentDistance < closestCoverDistance && _selectedEnemy.getPreviousTile() != coverList[i]){					
					closestCoverDistance = currentDistance;
					closestCover = coverList[i];
					//Debug.Log("Closest Covers: "+closestCover);
				}		
				
				if(currentDistance > furthestCoverDistance && _selectedEnemy.getPreviousTile() != coverList[i]){
					furthestCoverDistance = currentDistance;
					furthestCover = coverList[i];
				}		
			}
		}
		
		// If cannot find cover nearest to player,
		// find cover furthest from player
		if(closestCover == null){
			closestCover = furthestCover;
		}

		return closestCover;
	}

	function getForwardCoverTile(minDistance, mapReference):Tile{
		
		mapReference.checkRangeFrom(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var coverList:Array = mapReference.getCoverTiles(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var closestCovers:Array = new Array();
		
		var closestCoverDistance = 9999999;
		var currentDistance;
		
		// Set to run to closest tile by default
		var closestCover:Tile = null;	
		
		if(_selectedEnemy.getTurnsLeft() == 2){
			_selectedEnemy.setPreviousTile(_selectedEnemy.getTileReference());
		}
		
		for(var i:int=0; i<coverList.length; i++){
			var current_tile:Tile = coverList[i];
			//Debug.Log("Cover Tile: "+current_tile);
			if(!isTileFlankedByPlayer(current_tile)){				
				currentDistance = Vector2.Distance(current_tile.getCoord(), _coord);
				if(currentDistance > minDistance && currentDistance < closestCoverDistance && _selectedEnemy.getPreviousTile() != coverList[i]){					
					closestCoverDistance = currentDistance;
					closestCover = coverList[i];
					//Debug.Log("Closest Covers: "+closestCover);
				}			
			}
		}

		return closestCover;
	}
	
	function getRetreatCoverTile(mapReference):Tile{
		
		mapReference.checkRangeFrom(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var coverList:Array = mapReference.getCoverTiles(_selectedEnemy.getCoord(), _selectedEnemy.getMoveRadius());
		var closestCovers:Array = new Array();
		
		var furthestCoverDistance = 0;
		var furthestCover:Tile;	
	
		if(_selectedEnemy.getTurnsLeft() == 2){
			_selectedEnemy.setPreviousTile(_selectedEnemy.getTileReference());
		}
		
		for(var i:int=0; i<coverList.length; i++){
			var current_tile:Tile = coverList[i];
			if(!isTileFlankedByPlayer(current_tile)){	
				//Debug.Log("Cover Lists: "+coverList[i]);				
				var currentDistance = Vector2.Distance(current_tile.getCoord(), _coord);				
				if(currentDistance > furthestCoverDistance && _selectedEnemy.getPreviousTile() != coverList[i]){
					//Debug.Log("Closest Covers: "+closestCover);
					furthestCoverDistance = currentDistance;
					furthestCover = coverList[i];
				}				
			}
		}

		return furthestCover;
	}
	
	function isFlankedByPlayer(){
		var flankNumber:int = 0;
		var isFlanked:boolean = false;
		
		for(var i:int=0; i<_flankablePlayers.length; i++){
			if(_selectedEnemy.isFlanked(_flankablePlayers[i])){
				isFlanked = true;
				flankNumber++;
			}
		}	
		
		// prevent possible scenario of AI running forever
		/*if(flankNumber > 2)
			return false;*/
			
		return isFlanked;
	}
	
	function isTileFlankedByPlayer(checkTile:Tile){
		var flankNumber:int = 0;
		var isFlanked:boolean = false;
		
		for(var i:int=0; i<_flankablePlayers.length; i++){
			if(isFlanked(_flankablePlayers[i].getSurfaceCoord(), checkTile)){
				isFlanked = true;
				flankNumber++;
			}
		}	
								
		return isFlanked;
	}
	
	function isFlanked(playerCoord:Vector3, tileReference:Tile){
		var directionOfEnemy = playerCoord - tileReference.getSurfaceCoord();
		var isFlanked:boolean = true;
		//to the right
		if (directionOfEnemy.x > 0 ){
			isFlanked = !tileReference.getCoverDirection(Tile.COVER_DIRECTION_RIGHT);
			//Debug.Log("Flanked to right:" +tileReference+" | "+ isFlanked);
		}
		//to the left
		else if (directionOfEnemy.x < 0 ){
			isFlanked = !tileReference.getCoverDirection(Tile.COVER_DIRECTION_LEFT);
			//Debug.Log("Flanked to left:" + isFlanked);
		}
		
		//if not flanked, return false 	
		if (!isFlanked)
			return false;
		
		//to the top
		if (directionOfEnemy.z > 0){
			isFlanked = !tileReference.getCoverDirection(Tile.COVER_DIRECTION_TOP);
			//Debug.Log("Flanked to top:" + isFlanked);
		}
		else if (directionOfEnemy.z < 0){
			isFlanked = !tileReference.getCoverDirection(Tile.COVER_DIRECTION_BOTTOM);
			//Debug.Log("Flanked to bottom:" +tileReference+" | "+ isFlanked);
		}
		
		return isFlanked;
	}
	
	protected function checkDistanceRange(){
		
		for (var i = 0; i < _playerList .length; i++)
		{
			if(_playerList[i].isVisibleToEnemy() && !_playerList[i].getIsDead()){
				var dstCoord :Vector2 = _playerList[i].getCoord();
				var distance = Vector2.Distance(_srcCoord, dstCoord);
	
				if(distance < _closestDistance){
					_closestDistance = distance;
					_closestPlayer = _playerList[i];
					//Debug.Log("Closest Player: "+_closestPlayer);
				}
				//Debug.Log("Closest Distance: "+_closestDistance+" | Attack Range: "+_atkRange);
				if (distance <= _atkRange)
				{
					_isInRange = true;	
					_playersInRange.Push(_playerList[i]);
					
					// target players not in cover first
					if(!_playerList[i].isInCover() && distance <= _closestDistance){	
						_closestPlayer = _playerList[i];
					}				
					//Debug.Log("AI in range: "+_playerList [i]+" | "+distance);					
				}
				
				if(distance <= _detectFlankRadius && _previousFlankable.length == 0){
					_flankablePlayers.Push(_playerList[i]);
				}
			}
		}				
		
		if(_previousFlankable.length == 0){
			_selectedEnemy.setPreviousFlankable(_flankablePlayers);
		}else{
			_flankablePlayers = _previousFlankable;
		}
		
		if(_closestPlayer != null){
		
			//raycast to the list of players for visibility check
			var raySource:Vector3 = _selectedEnemy.getSurfaceCoord();				
			var rayDst:Vector3 = _closestPlayer.getSurfaceCoord();
			var hit:RaycastHit;
			//convert player to obstacle layer so that raycast will detect obstacles
			_closestPlayer.gameObject.layer = LayerMask.NameToLayer("Obstacle");
			//raycast 
			if (Physics.Raycast(raySource, rayDst - raySource, hit, _atkRange,1<<LayerMask.NameToLayer("Obstacle"))){
				//casted enemies, setSelectable(true)
				if(!hit.collider.gameObject.tag.Equals("PlayerCharacter")){
					_isInRange = false;
				}
			}
			_closestPlayer.gameObject.layer = LayerMask.NameToLayer("Player");
			_coord = _closestPlayer.getCoord();
			
		}
	}	
}